/*
 * Copyright (c) 2016. 恒昌互联网运营中心.
 */
package com.credithc.eagleeye.plugins.aspect;
import com.alibaba.fastjson.JSON;
import com.credithc.eagleeye.agent.factory.EagleEyeAgentFactory;
import com.credithc.eagleeye.agent.util.HostUtil;
import com.credithc.eagleeye.exception.EagleEyeIgnoreException;
import com.credithc.eagleeye.plugins.annotation.EagleEyeAnnotation;
import com.credithc.eagleeye.plugins.util.ClassUtil;
import com.credithc.eagleeye.plugins.util.LogUtil;
import com.credithc.eagleeye.thread.EagleEyeThreadContext;
import com.credithc.eagleeye.thread.TraceInfo;
import com.credithc.eagleeye.util.MixUtil;
import com.credithc.eagleeye.util.ThrowableToStringArray;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
/**
 * 鹰眼切面
 */
@SuppressWarnings("Duplicates")
@Aspect
@Component
public class EagleEyePluginAspect {
    private transient static final Logger logger = LoggerFactory.getLogger(EagleEyePluginAspect.class);
    /**
     * 参数和返回值字符最大长度
     */
    private int maxLength = 4000;

    public EagleEyePluginAspect() {
        logger.info("EagleEyePluginAspect init..");
    }

    @Pointcut("@annotation(com.credithc.eagleeye.plugins.annotation.EagleEyeAnnotation)")
    public void pointcut() {
    }

    @Around( "pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object resultObject=null;
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        EagleEyeAnnotation annotation = method.getAnnotation(EagleEyeAnnotation.class);
        if (annotation == null) {
            Object target = joinPoint.getTarget();
            Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
            method = target.getClass().getMethod(methodSignature.getName(), parameterTypes);
            if (method != null) {
                annotation = method.getAnnotation(EagleEyeAnnotation.class);
                if(annotation==null){
                    resultObject = joinPoint.proceed();
                    return resultObject;
                }
            }
        }

        TraceInfo traceInfo = EagleEyeThreadContext.getTraceInfo();
        if (traceInfo == null) {
            traceInfo = new TraceInfo();
        } else {
            traceInfo.addSequenceNo();
        }
        long beginTime = System.currentTimeMillis();
        RpcTraceInfoVo rpcTraceInfoVo = new RpcTraceInfoVo();
        try {
            rpcTraceInfoVo.setRequestDateTime(MixUtil.ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(new Date()));
            rpcTraceInfoVo.setTraceId(traceInfo.getTraceId());
            rpcTraceInfoVo.setRpcId(traceInfo.getRpcId());
            rpcTraceInfoVo.setRpcType(annotation.rpcType().getRpcName());
            rpcTraceInfoVo.setServiceCategory(annotation.serviceCategory());
            rpcTraceInfoVo.setServiceName(annotation.serviceName());
            rpcTraceInfoVo.setMethodName(methodSignature.getName());
            rpcTraceInfoVo.setClientHost(HostUtil.getIp());
            //请求的参数
            if(annotation.hasReqJson()) {
                StringBuilder requestBodySb = new StringBuilder();
                Object[] allArguments = joinPoint.getArgs();
                for(int i = 0; i < allArguments.length; i++) {
                    requestBodySb.append(String.format("args[%s]:", i));
                    requestBodySb.append(transform2JsonStr(allArguments[i]));
                    requestBodySb.append(";");
                }
                String requestJson = (requestBodySb.length() > maxLength) ? requestBodySb.substring(0, maxLength) + ".." : requestBodySb.toString();
                rpcTraceInfoVo.setRequestJson(requestJson);
            }
            rpcTraceInfoVo.setUserInfo(traceInfo.getUserInfo());
            if(StringUtils.isNotBlank(annotation.serverIp())){
                rpcTraceInfoVo.setServerHost(annotation.serverIp());
            }else{
                rpcTraceInfoVo.setServerHost(HostUtil.getIp());
            }
            //执行调用
            resultObject = joinPoint.proceed();
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
            rpcTraceInfoVo.setResult("OK");
            //返回结果
            if(annotation.hasResponseJson()) {
                String resultJson = transform2JsonStr(resultObject);
                if (resultJson.length() > maxLength) {
                    resultJson = resultJson.substring(0, maxLength) + "..";
                }
                rpcTraceInfoVo.setResponseJson(resultJson);
            }
        }catch(EagleEyeIgnoreException ex){
            rpcTraceInfoVo.setResult("OK");
        }catch(Exception ex){
            //ERROR
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
            rpcTraceInfoVo.setResult("ERROR");
            //异常的结果
            String traceStr[] = ThrowableToStringArray.convert(ex);
            StringBuilder builder = new StringBuilder();
            for(String trace : traceStr) {
                builder.append(trace);
                builder.append("\n");
            }
            rpcTraceInfoVo.setResponseJson(builder.toString());
            throw ex;
        }finally {
            EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
            LogUtil.printSplunkLog(rpcTraceInfoVo);
        }

        return resultObject;
    }

    private String transform2JsonStr(Object argument) {
        if (ClassUtil.servletPresent && (argument instanceof ServletRequest || argument instanceof ServletResponse)) {
            return "...";
        }
        if (ClassUtil.springWebPresent && (argument instanceof MultipartFile)) {
            return "...";
        }
        try {
            return JSON.toJSONString(argument);
        } catch (Exception ex) {
        }
        return "";
    }
}
